package com.aos.iot.socket.common.handler;

import cn.hutool.core.util.StrUtil;
import com.aos.iot.socket.common.ack.AckManager;
import com.aos.iot.socket.common.client.ClientBoxFactor;
import com.aos.iot.socket.common.client.IClientBox;
import com.aos.iot.socket.common.annotation.TcpEventListenerHub;
import com.aos.iot.socket.common.message.Message;
import com.aos.iot.socket.common.protocol.IProtocol;
import com.aos.iot.socket.common.scheduler.CancelableScheduler;
import com.aos.iot.socket.common.store.StoreFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class AbstractHandler<M extends Message, C extends SocketClient<M>> extends SimpleChannelInboundHandler<M> {

    protected final AckManager ackManager;

    protected final StoreFactory<M> storeFactory;

    protected final CancelableScheduler cancelableScheduler;

    protected final IProtocol<M> protocol;

    protected final ClientBoxFactor clientBoxFactor;

    protected final IClientBox<M, C> clientBox;

    public AbstractHandler(AckManager ackManager, StoreFactory<M> storeFactory, CancelableScheduler cancelableScheduler, IProtocol<M> protocol, ClientBoxFactor clientBoxFactor) {
        this.ackManager = ackManager;
        this.storeFactory = storeFactory;
        this.cancelableScheduler = cancelableScheduler;
        this.protocol = protocol;
        this.clientBoxFactor = clientBoxFactor;
        this.clientBox = clientBoxFactor.getClientBox(protocol.name());
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        cancelableScheduler.update(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) {
        if(!(ctx.channel() instanceof NioDatagramChannel)) {
            log.info("client join [{}-{}] ", ctx.channel().id().asLongText(), ctx.channel().remoteAddress());
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, M message) {
        C client = clientBox.get(ctx.channel());
        if(client == null) {
            client = createClient(ctx);
            registerClient(clientBox, client, message);
            clientBox.updateRemoteAddress(client, message.getSender());
            TcpEventListenerHub.invoke(protocol.name(), TcpEventListenerHub.CONNECT, ctx, ctx.channel(),
                    ctx.channel().id(), protocol, message, client, message.getDirection(), clientBox);
        }
        if(client != null) {
            message.setChannelId(client.getChannelId());
            clientBox.updateRemoteAddress(client, message.getSender());
            if(!ackManager.onAck(client, message)) {
                TcpEventListenerHub.invoke(protocol.name(), message.getCmd(), ctx, ctx.channel(), ctx.channel().id(),
                        protocol, message, client, message.getDirection(), clientBox);
            }
        }
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) {
        C client = clientBox.get(ctx.channel());
        if(client != null && StrUtil.equals(this.protocol.name(), client.getProtocol().name())) {
            client.disconnect();
            TcpEventListenerHub.invoke(protocol.name(), TcpEventListenerHub.DISCONNECT, ctx, ctx.channel(), ctx.channel().id(),
                    protocol, client);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("消息异常", cause);
    }

    public String getProtocolName() {
        return protocol.name();
    }

    public AbstractHandler<M, C> handler() {
        return this;
    }

    protected abstract C createClient(ChannelHandlerContext ctx);

    protected abstract void registerClient(IClientBox<M, C> clientBox, C client, M message);

}
